package 
{
	
	/**
	 * ...
	 * @author Felix Fleiß
	 */
	public class SingingToNote 
	{
		private  static var lowestFreqToNote:Number;
		private  static var highestFreqToNote:Number;
		public static var note:String = null;
		private var lastOctaveNormedNote:Number;
		public static const noteFreq:Array = new Array(
				   65.41,  69.30,  73.42,  77.78,  82.41,  87.31,  92.50,  98.00, 103.83, 110.00, 116.54, 123.47,
                  130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94,
                  261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
                  523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77);
		public static const noteName:Array = new Array(
				  "C",  "Des",    "D",   "Es",    "E",    "F",  "Ges",    "G",   "As",    "A",    "B",    "H",
                  "c",  "des",    "d",   "es",    "e",    "f",  "fes",    "g",   "as",    "a",    "b",    "h",
                  "c'","des'",   "d'",  "es'",   "e'",   "f'", "fes'",   "g'",  "as'",   "a'",   "b'",   "h'",
                  "c''", "des''",  "d''", "es''",  "e''",  "f''", "fes''",  "g''", "as''",  "a''",  "b''",  "h''");
				  
		public var sa:SpectrumAnalyzer;
		public function SingingToNote() {
			sa = new SpectrumAnalyzer();
			lowestFreqToNote  = (69 + 12*Math.log(noteFreq[0] / 440.0));//umrechnung zu tonhöhenklassen
			highestFreqToNote = (69 + 12 * Math.log(noteFreq[noteFreq.length - 1] / 440.0));
			lastOctaveNormedNote = 0.833333333;
		}
		public function setDrawSpectrum(doIt:Boolean):void {
			sa.doDrawSpectrum = doIt;
		}
		/**
		 * returns the sung note if it is in tolerance domain, else null
		 * returns the name of the note when the note is hit, else null
		 * @return
		 */
		public function getSungNote(tolerance:Number):String {
			if (sa.getMagnitudeOfLoudestFreq() <= sa.RECORD_DB_THRESHOLD) {
				note = null;
				return null;
			}
			var freq:Number;
			freq = sa.getLoudestFrequency();
			for (var i:int; i < noteFreq.length; i++) {
				if (Math.abs(mapFrequencyToNote(freq) - mapFrequencyToNote(noteFreq[i])) < tolerance) { //TODO: difference tolerance shall maybe not be liniear (try mapFrequencyToNote)
					note = noteName[i];
					return noteName[i];
				}
			}
			note = null;
			return null;
		}
		/**
		 * returns the sung frequency
		 * @return
		 */
		public function getSungFreq():Number {
			return sa.getLoudestFrequency();
		}
		/**
		 * returns the magnitude of the sung frequency in DB
		 * @return
		 */
		public function getDBofSungFreq():Number {
			return sa.getMagnitudeOfLoudestFreq();
		}
		/**
		 * returns a value between 0-1 representing the linear note position within a octave
		 * for rendering purpose
		 * @return
		 */
		public function getOctaveNormedNote():Number {
			var freq:Number = sa.getLoudestFrequency();
			if (freq < noteFreq[0] || freq > noteFreq[noteFreq.length-1])
				return lastOctaveNormedNote;
			if (sa.getMagnitudeOfLoudestFreq() <= sa.RECORD_DB_THRESHOLD)
				return lastOctaveNormedNote;
			lastOctaveNormedNote = mapFrequencyToNoteOctaveNorm(freq);
			return  lastOctaveNormedNote;
		}
		public static function mapFrequencyToNoteOctaveNorm(freq:Number):Number {
			var ret:Number = mapFrequencyToNoteNorm(freq);
			var coveredOctaves:Number = (noteFreq.length / 12.0);
			var normedOctave:Number = 0.2553;// 1 / coveredOctaves;//i assume 12 notes per octave
			while (ret > normedOctave) {
				ret -= normedOctave;
			}
			ret *= coveredOctaves;
			return ret+0.03;
		}
		/**
		 * maps frequency to linear scala reaching from 0 tio 1, where 0 is lowest and 1 is highest frequency of freq array
		 * @param	freq
		 * @return
		 */
		public static function mapFrequencyToNoteNorm(freq:Number):Number{
			return ((mapFrequencyToNote(freq) - lowestFreqToNote) / (highestFreqToNote - lowestFreqToNote));
		}
		/**
		 * maps frequency to linear scala
		 * @param	freq
		 * @return
		 */
		public static function mapFrequencyToNote(freq:Number):Number{
			return (69 + 12*Math.log(freq / 440.0));
		}
	}
	
}